using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;

namespace zeroflag.Windows
{
	public partial class TreeView<T> : UserControl
	//where T : IEquatable<T>, IEquatable<object>
	{
		public TreeView()
		{
			InitializeComponent();
		}


		#region Node

		private T m_Node;

		/// <summary>
		/// The root node displayed in the tree view.
		/// </summary>
		public T Node
		{
			get { return m_Node; }
			set
			{
				if (m_Node == null || m_Node.Equals(value))
				{
					this.OnNodeChanged(m_Node, m_Node = value);
				}
			}
		}

		#region NodeChanged event
		public delegate void NodeChangedHandler(object sender, T oldvalue, T newvalue);

		private event NodeChangedHandler m_NodeChanged;
		/// <summary>
		/// Occurs when Node changes.
		/// </summary>
		public event NodeChangedHandler NodeChanged
		{
			add { this.m_NodeChanged += value; }
			remove { this.m_NodeChanged -= value; }
		}

		/// <summary>
		/// Raises the NodeChanged event.
		/// </summary>
		protected virtual void OnNodeChanged(T oldvalue, T newvalue)
		{
			this.GetChildEnumerator = FindChildEnumerator(newvalue);
			// if there are event subscribers...
			if (this.m_NodeChanged != null)
			{
				// call them...
				this.m_NodeChanged(this, oldvalue, newvalue);
			}

			this.RefreshTreeView();
		}
		#endregion NodeChanged event
		#endregion Node

		#region GetChildEnumerator
		protected delegate System.Collections.IEnumerable GetChildEnumeratorHandler(T node);
		protected delegate System.Collections.IEnumerable GetPropertyChildEnumeratorHandler(T node, object value);

		GetChildEnumeratorHandler m_GetChildEnumerator;

		protected GetChildEnumeratorHandler GetChildEnumerator
		{
			get
			{
				if (m_GetChildEnumerator == null)
					m_GetChildEnumerator = this.FindChildEnumerator(this.Node);
				return m_GetChildEnumerator;
			}
			set { m_GetChildEnumerator = value; }
		}

		protected GetChildEnumeratorHandler FindChildEnumerator(T value)
		{
			System.Collections.IEnumerable enumerator = null;
			if (value == null)
				return null;
			else if ((enumerator = value as System.Collections.IEnumerable) != null)
				// check if the current node is an enumerator...
				return delegate(T node) { return ((System.Collections.IEnumerable)node); };
			else
			{
				GetChildEnumeratorHandler inner = null;
				inner = this.FindEnumeratorProperty(value);
				if (inner != null)
					return delegate(T node) { return inner(node); };
			}
			return null;
		}

		protected GetChildEnumeratorHandler FindEnumeratorProperty(object value)
		{
			foreach (System.Reflection.PropertyInfo property in value.GetType().GetProperties())
			{
				if (!typeof(T).Equals(typeof(object)))
				{
					List<Type> interfaces = new List<Type>(property.PropertyType.GetInterfaces());
					if (interfaces.Contains(typeof(IEnumerable<T>)))
					{
						return delegate(T node) { return (System.Collections.IEnumerable)property.GetValue(node, null); };
					}
					else if (property.PropertyType.IsSubclassOf(typeof(IEnumerable<T>)))
					{
						return delegate(T node) { return (System.Collections.IEnumerable)property.GetValue(node, null); };
					}
				}
				else
				{
					List<Type> interfaces = new List<Type>(property.PropertyType.GetInterfaces());
					if (interfaces.Contains(typeof(System.Collections.IEnumerable)))
					{
						return delegate(T node) { return (System.Collections.IEnumerable)property.GetValue(value, null); };
					}
					if (property.PropertyType.IsSubclassOf(typeof(System.Collections.IEnumerable)))
					{
						return delegate(T node) { return (System.Collections.IEnumerable)property.GetValue(node, null); };
					}
					else if (property.PropertyType.IsSubclassOf(typeof(IEnumerable<>)))
					{
						return delegate(T node) { return (IEnumerable<T>)property.GetValue(node, null); };
					}

				}
			}
			return null;
		}
		#endregion GetChildEnumerator

		#region Refresh
		public virtual void RefreshTreeView()
		{
			if (this.Node != null)
			{
				TreeViewItem<T> root = this.BuildViewNode(this.Node);

				this.treeViewControl.Nodes.Clear();
				this.treeViewControl.Nodes.Add(root);
				root.Expand();
			}
			else
				this.treeViewControl.Nodes.Clear();
		}

		protected virtual TreeViewItem<T> BuildViewNode(T node)
		{
			TreeViewItem<T> treenode = new TreeViewItem<T>(node);

			this.TreeNodes[treenode] = node;
			this.Nodes[node] = treenode;
			try
			{
				System.Collections.IEnumerable children = this.GetChildEnumerator(node);

				foreach (T child in children)
				{
					treenode.Nodes.Add(this.BuildViewNode(child));
				}
			}
			catch (Exception exc)
			{
				System.Diagnostics.Debug.WriteLine(exc);
				Console.WriteLine(exc);
			}

			return treenode;
		}
		#endregion Refresh

		#region TreeViewItem<T> <=> Node
		Dictionary<TreeViewItem<T>, T> m_TreeNodes = new Dictionary<TreeViewItem<T>, T>();

		protected Dictionary<TreeViewItem<T>, T> TreeNodes
		{
			get { return m_TreeNodes; }
			set { m_TreeNodes = value; }
		}

		Dictionary<T, TreeViewItem<T>> m_Nodes = new Dictionary<T, TreeViewItem<T>>();

		protected Dictionary<T, TreeViewItem<T>> Nodes
		{
			get { return m_Nodes; }
			set { m_Nodes = value; }
		}
		#endregion TreeViewItem<T> <=> Node
	}
}
